﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using System.IO.Ports;
using n_MySerialPort;
using System.Text.RegularExpressions;
using System.Globalization;
using n_AppForm;
using n_BigiotForm;
using System.Text;
using n_Modbus;

namespace n_UARTForm
{
	/// <summary>
	/// Description of MainForm.
	/// </summary>
	public partial class UARTForm : Form
	{
		//串口接收事件(仿真时由硬件发送)
		public delegate bool D_GetSimStatus();
		public static D_GetSimStatus deleGetSimStatus;
		
		//串口下发事件(仿真时由硬件接收)
		public delegate void D_SendData( byte b );
		public static D_SendData deleSendData;
		
		//手机直连发送数据
		AppForm AppBox;
		
		//数据连接到贝壳物联
		BigiotForm BigiotBox;
		
		public bool SingleMode = true;
		
		//串行端口
		System.IO.Ports.SerialPort port;
		//数据发送缓冲区
		byte[] buffer;
		
		//Modbus缓冲区
		byte[] modbus_buffer;
		int mod_len;
		
		byte[] SimBuffer;
		int SimWriteIndex;
		int SimReadIndex;
		
		bool isSim;
		public bool isOpen;
		
		bool GBstart;
		int LastData;
		
		int PerLineHeight;
		
		StringBuilder BufferString;
		
		int ErrTick;
		int ExtWidth;
		
		enum TOOL_TYPE {
			NONE, MODBUS, ESP8266
		};
		TOOL_TYPE ToolType;
		
		//主窗口
		public UARTForm()
		{
			InitializeComponent();
			
			this.comboBox波特率.Text = "9600";
			//RefreshUARTList();
			buffer = new byte[ 2000 ];
			SimBuffer = new byte[ 2000 ];
			
			modbus_buffer = new byte[100];
			mod_len = 0;
			
			BufferString = new StringBuilder( 3000 );
			
			isSim = false;
			isOpen = false;
			GBstart = false;
			
			SingleMode = false;
			
			richTextBox接收.Text = "1\n2";
			System.Drawing.Point ptLine1　　//第一行第一个字节的坐标
				= richTextBox接收.GetPositionFromCharIndex( richTextBox接收.GetFirstCharIndexFromLine(0));
			System.Drawing.Point ptLine2　　//第二行第一个字节的坐标
				= richTextBox接收.GetPositionFromCharIndex(richTextBox接收.GetFirstCharIndexFromLine(1));
			PerLineHeight = ptLine2.Y - ptLine1.Y;
			
			richTextBox接收.Text = "";
			
			AppBox = new AppForm();
			AppBox.d_TextSend = SendText;
			
			BigiotBox = new BigiotForm();
			BigiotBox.d_TextSend = SendText;
			BigiotBox.d_SysSend = SysSend;
			
			panel1.Dock = DockStyle.Fill;
			Width = 765;
			
			Modbus_Init();
			
			TextBoxTextChanged( null, null );
		}
		
		//运行
		public void Run()
		{
			ToolType = TOOL_TYPE.NONE;
			
			panel8266.Visible = false;
			panelModbus.Visible = false;
			ExtWidth = 0;
			LabelMesClick( null, null );
			Modbus_Clear();
			this.Visible = true;
		}
		
		void ErrorShow( string m )
		{
			if( ErrTick == 0 ) {
				labelMes.Text += "点击清除信息\n";
			}
			ErrTick++;
			labelMes.Text += System.DateTime.Now.Hour.ToString().PadLeft( 2, '0' ) + ":" + System.DateTime.Now.Minute.ToString().PadLeft( 2, '0' ) + ":" + System.DateTime.Now.Second.ToString().PadLeft( 2, '0' ) + "\n-> " + m + "\n";
		}
		
		//数据发送委托
		void SendText( string s )
		{
			radioButton字符形式.Checked = true;
			richTextBox发送.Text = s;
		}
		
		//系统发送
		void SysSend()
		{
			Button发送Click( null, null );
		}
		
		//打开端口
		void Open( string COMNumber, int Baud )
		{
			if( COMNumber == "LB-COM0" ) {
				isSim = true;
				return;
			}
			
			isSim = false;
			
			port = new System.IO.Ports.SerialPort( COMNumber );
			port.BaudRate = Baud;
			port.DataBits = 8;
			port.Parity = System.IO.Ports.Parity.None;
			port.StopBits = System.IO.Ports.StopBits.One;
			port.ReadTimeout = 1;
			
			if( checkBoxDTR_RTS.Checked ) {
				port.DtrEnable = true;
				port.RtsEnable = true;
			}
			else {
				port.DtrEnable = false;
				port.RtsEnable = false;
			}
			try {
				port.Open();
				port.DataReceived += new SerialDataReceivedEventHandler( DataReceived );
				timer1.Enabled = true;
			}
			catch {
				ErrorShow( "串口打开失败, 可能被其他程序占用, 请关闭其他使用串口的软件后重试" );
				port = null;
			}
			
//			port = new Port();
//			port.PortNum = COMNumber;
//			port.BaudRate = Baud;
//			port.ByteSize = 8;
//			port.Parity = (byte)System.IO.Ports.Parity.None;
//			port.StopBits = (byte)System.IO.Ports.StopBits.One;
//			port.ReadTimeout = 1;
//			try {
//				if( port.Opened ) {
//					port.Close();
//					port.Open(); //打开串口
//				}
//				else {
//					port.Open();//打开串口
//				}
//			}
//			catch {
//				ErrorShow( "串口初始化失败" );
//			}
			
			//timer.Enabled = true;
		}
		
		//发送字符串形式
		void SendString( string text )
		{
			int n = RefreshBuffer( text );
			SentBuffer( n );
		}
		
		//发送十进制数字形式
		void SendDec( string text )
		{
			text = text.Trim( ' ' );
			string[] cut = text.Split( ' ');
			try{
				for( int i = 0; i < cut.Length; ++i ) {
					buffer[ i ] = byte.Parse( cut[ i ] );
				}
			}
			catch {
				ErrorShow( "输入的数据格式不正确" );
				return;
			}
			SentBuffer( cut.Length );
		}
		
		//发送十六进制数字形式
		void SendHex( string text )
		{
			text = text.Trim( ' ' );
			string[] cut = text.Split( ' ');
			try {
				for( int i = 0; i < cut.Length; ++i ) {
					buffer[ i ] = byte.Parse( cut[ i ], System.Globalization.NumberStyles.HexNumber );
				}
			}
			catch {
				ErrorShow( "输入的数据格式不正确" );
				return;
			}
			SentBuffer( cut.Length );
		}
		
		//发送缓冲区
		void SentBuffer( int Length )
		{
			if( isSim ) {
				if( deleSendData != null ) {
					for( int i = 0; i < Length; ++i ) {
						deleSendData( buffer[i] );
					}
				}
			}
			else {
				port.Write( buffer, 0, Length );
			}
		}
		
		//更新缓冲区
		int RefreshBuffer( string text )
		{
			int n = 0;
			for( int i = 0; i < text.Length; ++i ) {
				
				char b = text[ i ];
				
				if( b == '\n' ) {
					buffer[ n ] = 0x0D;
					n++;
					buffer[ n ] = 0x0A;
					n++;
					continue;
				}
				if( b == '\\' ) {
					
					if( text.Length <= i + 1 ) {
						//...
					}
					else if( text[ i + 1 ] == '\\' ) {
						b = '\\';
						i++;
					}
					else if( text[ i + 1 ] == '"' ) {
						b = '"';
						i++;
					}
					else if( text[ i + 1 ] == 'r' ) {
						b = (char)0x0D;
						i++;
					}
					else if( text[ i + 1 ] == 'n' ) {
						b = (char)0x0A;
						i++;
					}
					else if( text[ i + 1 ] == 'z' ) {
						b = (char)0x1A;
						i++;
					}
					else {
						ErrorShow( "未知的类型字符: " + text[ i + 1 ] );
					}
				}
				int CODE = (int)b;
				if( CODE > 255 ) {
					byte[] gbk = System.Text.Encoding.GetEncoding("GBK").GetBytes( b.ToString() );
					buffer[ n ] = gbk[0];
					n++;
					buffer[ n ] = gbk[1];
					n++;
				}
				else {
					buffer[ n ] = (byte)b;
					n++;
				}
			}
			this.labelShowHex.Text = "";
			for( int i = 0; i < n; ++i ) {
				this.labelShowHex.Text += buffer[i].ToString( "X" ).PadLeft( 2, '0' ) + " ";
			}
			return n;
		}
		
		//数据接收事件
		void DataReceived(object sender, EventArgs e)
		{
			/*
			try {
				int n = port.Read( buffer, 0, 10 );
				if( n == 0 ) {
					return;
				}
				for( int i = 0; i < n; ++i ) {
					byte b = buffer[ i ];
					AddByte( b );
				}
			}
			catch {
				
			}
			*/
		}
		
		//添加一个字节到显示窗口
		void AddByte( byte b )
		{
			if( !isOpen ) {
				return;
			}
			
			if( this.radioButton显示_字符形式.Checked ) {
				
				if( checkBoxAutoClear.Checked ) {
				int MaxNumber = richTextBox接收.Height / PerLineHeight;
					if( richTextBox接收.Lines.Length >= MaxNumber - 1 ) {
						richTextBox接收.Clear();
					}
				}
				
				int Data = b;
				if( GBstart ) {
					GBstart = false;
					char c = System.Text.Encoding.GetEncoding("GBK").GetChars( new byte[] {(byte)LastData, (byte)Data} )[0];
					BufferString.Append( c );
				}
				else {
					if( Data > 127 ) {
						GBstart = true;
						LastData = Data;
						return;
					}
					if( Data != 0x0A ) {
						BufferString.Append( (char)Data );
					}
				}
			}
			else if( this.radioButton显示_十进制数字形式.Checked ) {
				BufferString.Append( b.ToString() + " " );
			}
			else if( this.radioButton显示_十六进制数字形式.Checked ) {
				BufferString.Append( b.ToString( "X" ).PadLeft( 2, '0' ) + " " );
			}
			else {
				ErrorShow( "显示异常,未设置显示格式" );
			}
		}
		
		public void ReceiveByte( byte b )
		{
			if( !isOpen ) {
				return;
			}
			SimBuffer[SimWriteIndex] = b;
			SimWriteIndex++;
			if( SimWriteIndex == SimBuffer.Length ) {
				SimWriteIndex = 0;
			}
			
			Timer1Tick( null, null );
		}
		
		int tick = 0;
		bool Changed = false;
		void Timer1Tick(object sender, EventArgs e)
		{
			if( !isOpen ) {
				return;
			}
			tick++;
			if( tick == 5 ) {
				tick = 0;
				
				// Focus 函数会抢夺其他控件焦点; ScrollToCaret 不能太频繁调用 否则数据量大可能死机 2020.11.26
				
				//this.richTextBox接收.SelectionStart = this.richTextBox接收.Text.Length;
				//this.richTextBox接收.SelectionLength = 0;
				//this.richTextBox接收.Focus();
				if( Changed ) {
					Changed = false;
					try {
						this.richTextBox接收.SelectionStart = this.richTextBox接收.Text.Length;
						this.richTextBox接收.SelectionLength = 0;
						this.richTextBox接收.ScrollToCaret();
					}
					catch {
						this.Text += " SC-ERROR! ";
					}
				}
			}
			int n = 0;
			if( !isSim ) {
				if( port != null && port.IsOpen ) {
					try {
						n = port.Read( buffer, 0, buffer.Length - 1 );
					}
					catch {
					}
				}
			}
			else {
				int wi = SimWriteIndex;
				if( wi < SimReadIndex ) {
					wi += SimBuffer.Length;
				}
				for( int i = SimReadIndex; i < wi; ++i ) {
					buffer[n] = SimBuffer[i % SimBuffer.Length];
					n++;
				}
				SimReadIndex = SimWriteIndex;
			}
			if( n == 0 ) {
				return;
			}
			if( isSim ) {
				Mod_Tick = p_VM.VM.Tick_ms;
			}
			else {
				Mod_Tick = 0;
			}
			BufferString.Length = 0;
			for( int i = 0; i < n; ++i ) {
				byte b = buffer[ i ];
				AddByte( b );
				
				if( ToolType == TOOL_TYPE.MODBUS && mod_len < modbus_buffer.Length ) {
					modbus_buffer[mod_len] = b;
					mod_len++;
				}
			}
			string sss = BufferString.ToString();
			this.richTextBox接收.AppendText( sss );
			if( sss.IndexOf( "\n" ) != -1 ) {
				Changed = true;
			}
			////this.richTextBox接收.ScrollToCaret();
		}
		
		//窗体关闭事件
		void UARTFormFormClosing(object sender, FormClosingEventArgs e)
		{
			if( !SingleMode ) {
				Width -= ExtWidth;
				this.Visible = false;
				e.Cancel = true;
				return;
			}
			
			try {
				if( port != null && port.IsOpen ) {
					port.Close();
				}
			}
			catch {
				ErrorShow( "CLOSE ERROR" );
				port = null;
			}
			//this.Visible = false;
			//e.Cancel = true;
			//timer.Enabled = false;
		}
		
		public void Button打开串口Click(object sender, EventArgs e)
		{
			if( button打开串口.Text == "关闭串口" ) {
				this.timerAutoSend.Enabled = false;
				timer1.Enabled = false;
				timerModbus.Enabled = false;
				if( !isSim ) {
					try {
						//port.DiscardInBuffer();
						//port.DiscardOutBuffer();
						port.DataReceived -= new SerialDataReceivedEventHandler( DataReceived );
						port.Close();
						port = null;
					}
					catch {
						ErrorShow( "串口关闭失败!" );
					}
				}
				isOpen = false;
				button打开串口.Text = "打开串口";
				button打开串口.BackColor = Color.LightGray;
				return;
			}
			else {
				try{
					string Name = this.comboBox串口号.Text;
					string Baud = this.comboBox波特率.Text;
					int BaudN = int.Parse( Baud );
					Open( MySerialPort.GetComName( Name ), BaudN );
				}
				catch {
					ErrorShow( "串口打开失败!" );
					return;
				}
				if( !isSim && port == null ) {
					return;
				}
				isOpen = true;
				button打开串口.Text = "关闭串口";
				button打开串口.BackColor = Color.LimeGreen;
				this.timerAutoSend.Enabled = this.checkBox自动发送.Checked;
				//timerModbus.Enabled = true;
			}
		}
		
		void Button发送Click(object sender, EventArgs e)
		{
			if( !isOpen ) {
				ErrorShow( "请先打开串口" );
				return;
			}
			string te = this.richTextBox发送.Text;
			
			if( this.radioButton字符形式.Checked ) {
				SendString( te );
			}
			else if( this.radioButton十进制数字.Checked ) {
				SendDec( te );
			}
			else if( this.radioButton十六进制数字.Checked ) {
				SendHex( te );
			}
			else {
				ErrorShow( "发送异常, 未设置发送格式" );
			}
		}
		
		void Button清空Click(object sender, EventArgs e)
		{
			this.richTextBox接收.Text = "";
		}
		
		void CheckBox自动发送CheckedChanged(object sender, EventArgs e)
		{
			this.timerAutoSend.Enabled = (this.checkBox自动发送.Checked && port != null);
		}
		
		void TimerAutoSendTick(object sender, EventArgs e)
		{
			if( port != null ) {
				Button发送Click( null, null );
			}
		}
		
		void TextBoxSendTTextChanged(object sender, EventArgs e)
		{
			float S = 0;
			if( float.TryParse( this.textBoxSendT.Text, out S ) && (int)( S * 1000 ) != 0 ) {
				this.timerAutoSend.Interval = (int)( S * 1000 );
				this.textBoxSendT.ForeColor = Color.Black;
			}
			else {
				//ErrorShow( "参数格式不正确: " + this.textBoxSendT.Text );
				//this.textBoxSendT.Text = this.timerAutoSend.Interval.ToString();
				this.textBoxSendT.ForeColor = Color.Red;
			}
		}
		
		void ComboBox串口号Click(object sender, EventArgs e)
		{
			this.comboBox串口号.Items.Clear();
			
			if( deleGetSimStatus != null && deleGetSimStatus() ) {
				this.comboBox串口号.Items.Add( "LB-COM0 linkboy仿真串口号" );
				return;
			}
			
			//通过WMI获取COM端口
			string[] ss = MySerialPort.GetFullNameList();
			if( ss != null ) {
				this.comboBox串口号.Items.AddRange( ss );
			}
		}
		
		void RichTextBox发送TextChanged(object sender, EventArgs e)
		{
			if( this.radioButton字符形式.Checked ) {
				int n = RefreshBuffer( this.richTextBox发送.Text );
			}
		}
		
		void ButtonATClick(object sender, EventArgs e)
		{
			string s = ((Button)sender).Text;
			s = s.Split( '\n' )[1];
			radioButton字符形式.Checked = true;
			richTextBox发送.Text = s;
		}
		
		void ButtonAppClick(object sender, EventArgs e)
		{
			AppBox.Run();
		}
		
		void ButtonBigiotClick(object sender, EventArgs e)
		{
			BigiotBox.Run();
		}
		
		void LabelMesClick(object sender, EventArgs e)
		{
			ErrTick = 0;
			labelMes.Text = "";
		}
		
		//=================================================================
		
		long Mod_Tick;
		
		void Modbus_Init()
		{
			Mod_Tick = 0;
		}
		
		void Modbus_Clear()
		{
			labelModbusR.Text = "点击清空数据\n";
		}
		
		void Modbus_Add( string mes )
		{
			labelModbusR.Text += mes + "\n";
		}
		
		void ButtonModbusClick(object sender, EventArgs e)
		{
			int w = Width;
			panel8266.Visible = false;
			panelModbus.Visible = false;
			Width = w + panelModbus.Width - ExtWidth;
			panel8266.Visible = false;
			panelModbus.Visible = true;
			ExtWidth = panelModbus.Width;
			Refresh();
			
			ToolType = TOOL_TYPE.MODBUS;
		}
		
		void ButtonPackClick(object sender, EventArgs e)
		{
			try {
			byte address = Convert.ToByte( textBoxSlaAddr.Text, 16 );//地址码
			byte funcd = Convert.ToByte( textBoxFuncCode.Text, 16 );//地址码 //0x03;//命令帧
			UInt16 regAddr = Convert.ToUInt16( textBoxStartAddr.Text, 16 );//寄存器地址
			UInt16 regNum = Convert.ToUInt16( textBoxNumber.Text, 16 );//寄存器数量
			byte len = 8;
			
			byte[] frame = Modbus.Get_MyFrame( address, funcd, regAddr, regNum, len );
			string r = Modbus.ToHexString( frame, len );
			this.richTextBox发送.Text = r;
			}
			catch {
				MessageBox.Show( "请输入正确的数字!" );
			}
			this.radioButton十六进制数字.Checked = true;
			Button发送Click( null, null );
		}
		
		void LabelModbusRClick(object sender, EventArgs e)
		{
			Modbus_Clear();
		}
		
		void TimerModbusTick(object sender, EventArgs e)
		{
			if( ToolType != TOOL_TYPE.MODBUS ) {
				return;
			}
			if( isSim ) {
				
				Modbus_Add( p_VM.VM.Tick_ms + " + " + Mod_Tick + "  " );
				
				if( System.Math.Abs( p_VM.VM.Tick_ms - Mod_Tick ) < 100 ) {
					return;
				}
				p_VM.VM.Tick_ms = Mod_Tick;
			}
			else {
				Mod_Tick++;
				if( Mod_Tick < 10 ) {
					return;
				}
				Mod_Tick = 0;
			}
			
			//如果上次没收到数据, 表示一个数据帧已结束
			if( mod_len != 0 ) {
				//string r = Modbus.SetText( modbus_buffer, mod_len );
				string r = "LEN:" + mod_len + "  ";
				for( int i = 0; i < mod_len; ++i ) {
					r += modbus_buffer[i] + ", ";
				}
				Modbus_Add( r );
				mod_len = 0;
			}
		}
		
		void TextBoxTextChanged(object sender, EventArgs e)
		{
			try {
			byte address = Convert.ToByte( textBoxSlaAddr.Text, 16 );//地址码
			textBoxSlaAddr10.Text = address.ToString();
			}
			catch {
				textBoxSlaAddr10.Text = "数值错误";
			}
			try {
			byte funcd = Convert.ToByte( textBoxFuncCode.Text, 16 );//地址码 //0x03;//命令帧
			textBoxFuncCode10.Text = funcd.ToString();
			}
			catch {
				textBoxFuncCode10.Text = "数值错误";
			}
			try {
			UInt16 regAddr = Convert.ToUInt16( textBoxStartAddr.Text, 16 );//寄存器地址
			textBoxStartAddr10.Text = regAddr.ToString();
			}
			catch {
				textBoxStartAddr10.Text = "数值错误";
			}
			try {
			UInt16 regNum = Convert.ToUInt16( textBoxNumber.Text, 16 );//寄存器数量
			textBoxNumber10.Text = regNum.ToString();
			}
			catch {
				textBoxNumber10.Text = "数值错误";
			}
		}
		
		//-------------------------------------------------------
		
		void Button8266Click(object sender, EventArgs e)
		{
			int w = Width;
			panel8266.Visible = false;
			panelModbus.Visible = false;
			Width = w + panel8266.Width - ExtWidth;
			panel8266.Visible = true;
			panelModbus.Visible = false;
			ExtWidth = panel8266.Width;
			Refresh();
			
			ToolType = TOOL_TYPE.ESP8266;
		}
	}
}

